package com.hsy.order.base.http

import okio.Buffer
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit
import com.flux.utils.LogUtil

import okhttp3.internal.http.HttpHeaders
import okhttp3.*
import java.io.EOFException
import java.io.IOException
import java.net.URLDecoder
import java.nio.charset.UnsupportedCharsetException

/**
 * 添加公共参数和log的网络拦截器
 * Created on 2017/12/4.
 *
 * @author Yang
 */
object HttpInterceptor : Interceptor {

    private val UTF8 = Charset.forName("UTF-8")

    enum class Level {
        /** No logs.  */
        NONE,
        /**
         * Logs request and response lines.
         *
         *
         * Example:
         * <pre>`--> POST /greeting http/1.1 (3-byte body)
         *
         * <-- 200 OK (22ms, 6-byte body)
        `</pre> *
         */
        BASIC,
        /**
         * Logs request and response lines and their respective headers.
         *
         *
         * Example:
         * <pre>`--> POST /greeting http/1.1
         * Host: example.com
         * Content-Type: plain/text
         * Content-Length: 3
         * --> END POST
         *
         * <-- 200 OK (22ms)
         * Content-Type: plain/text
         * Content-Length: 6
         * <-- END HTTP
        `</pre> *
         */
        HEADERS,
        /**
         * Logs request and response lines and their respective headers and bodies (if present).
         *
         *
         * Example:
         * <pre>`--> POST /greeting http/1.1
         * Host: example.com
         * Content-Type: plain/text
         * Content-Length: 3
         *
         * Hi?
         * --> END GET
         *
         * <-- 200 OK (22ms)
         * Content-Type: plain/text
         * Content-Length: 6
         *
         * Hello!
         * <-- END HTTP
        `</pre> *
         */
        BODY
    }

    @Volatile private var level = Level.NONE

    /** Change the level at which this interceptor logs.  */
    fun setLevel(level: Level?): HttpInterceptor {
        if (level == null) throw NullPointerException("level == null. Use Level.NONE instead.")
        this.level = level
        return this
    }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val level = this.level

        val request = chain.request()
        if (level == Level.NONE) {
            return chain.proceed(request)
        }

        val logBody = level == Level.BODY
        val logHeaders = logBody || level == Level.HEADERS

        /*
            打印开始开始请求
         */
        val requestBody = request.body()
        val hasRequestBody = requestBody != null

        val connection = chain.connection()
        val protocol = if (connection != null) connection.protocol() else Protocol.HTTP_1_1
        val requestMessage = request.method() + ' ' + request.url() + ' ' + protocol
        var requestStartMessage = "--> $requestMessage"
        if (hasRequestBody) {
            requestStartMessage += " (" + requestBody!!.contentLength() + "-byte body)"
        }
        LogUtil.i("start sending Request: $requestStartMessage")

        /*
            请求中,计算请求时间并打印
         */
        val startNs = System.nanoTime()
        val response: Response
        try {
            response = chain.proceed(request)
        } catch (e: Exception) {
            LogUtil.e("<-- HTTP FAILED: $e")
            throw e
        }

        val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)

        /*
            请求完后打印RequestBody
         */
        LogUtil.printDivider()

        if (logHeaders) {
            var hasFile = false
            if (hasRequestBody) {
                // Request body headers are only present when installed as a network interceptor. Force
                // them to be included (when available) so there values are known.
                if (requestBody!!.contentType() != null) {
                    val contentType = requestBody.contentType()!!.toString()
                    LogUtil.i("Content-Type: $contentType")
                    hasFile = contentType.startsWith("multipart")
                }
                if (requestBody.contentLength() != -1L) {
                    LogUtil.i("Content-Length: " + requestBody.contentLength())
                }
            }

            if (!logBody || !hasRequestBody) {
                LogUtil.i("--> END " + request.method())
            } else if (bodyEncoded(request.headers())) {
                LogUtil.i("--> END " + request.method() + " (encoded body omitted)")
            } else {
                val buffer = Buffer()
                requestBody!!.writeTo(buffer)

                var charset = UTF8
                val contentType = requestBody.contentType()
                if (contentType != null) {
                    charset = contentType.charset(UTF8)
                }

                if (isPlaintext(buffer)) {
                    if (hasFile) {
                        LogUtil.i("params: " + buffer.readString(charset))
                    }else{
                        LogUtil.i("params: " + URLDecoder.decode(buffer.readString(charset), "UTF-8"))
                    }
                    LogUtil.i("--> END " + request.method() + " (" + requestBody.contentLength() + "-byte body)")
                } else {
                    LogUtil.i("--> END "
                            + request.method()
                            + " (binary "
                            + requestBody.contentLength()
                            + "-byte body omitted)")
                }
            }
        }

//        请求完后打印ResponseBody
        val responseBody = response.body()
        val contentLength = responseBody!!.contentLength()
        val bodySize = if (contentLength != -1L) contentLength.toString() + "-byte" else "unknown-length"
        LogUtil.i("<-- " + response.code() + ' ' + response.message() + ' ' + response.request()
                .url() + " (" + tookMs + "ms" + (if (logHeaders) ", $bodySize body" else "") + ')')

        if (logHeaders) {

            if (!logBody || !HttpHeaders.hasBody(response)) {
                LogUtil.i("<-- END HTTP")
            } else if (bodyEncoded(response.headers())) {
                LogUtil.i("<-- END HTTP (encoded body omitted)")
            } else {
                val source = responseBody.source()
                source.request(java.lang.Long.MAX_VALUE) // Buffer the entire body.
                val buffer = source.buffer()

                var charset = UTF8
                val contentType = responseBody.contentType()
                if (contentType != null) {
                    try {
                        charset = contentType.charset(UTF8)
                    } catch (e: UnsupportedCharsetException) {
                        LogUtil.i("Couldn't decode the response body; charset is likely malformed.")
                        LogUtil.i("<-- END HTTP")

                        return response
                    }
                }

                if (!isPlaintext(buffer)) {
                    LogUtil.i("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)")
                    return response
                }

                if (contentLength != 0L) {
                    LogUtil.printJson(buffer.clone().readString(charset))
                }

                LogUtil.i("<-- END HTTP: " + requestMessage + " (" + buffer.size() + "-byte body)")
            }

            LogUtil.printDivider()
        }

        return response
    }

    /**
     * Returns true if the body in question probably contains human readable text. Uses a small sample
     * of code points to detect unicode control characters commonly used in binary file signatures.
     */
    @Throws(EOFException::class)
    private fun isPlaintext(buffer: Buffer): Boolean {
        try {
            val prefix = Buffer()
            val byteCount = if (buffer.size() < 64) buffer.size() else 64
            buffer.copyTo(prefix, 0, byteCount)
            for (i in 0..15) {
                if (prefix.exhausted()) {
                    break
                }
                val codePoint = prefix.readUtf8CodePoint()
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false
                }
            }
            return true
        } catch (e: EOFException) {
            return false // Truncated UTF-8 sequence.
        }

    }

    private fun bodyEncoded(headers: Headers): Boolean {
        val contentEncoding = headers.get("Content-Encoding")
        return contentEncoding != null && !contentEncoding.equals("identity", ignoreCase = true)
    }
}